using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using Volo.Abp.Testing;

namespace UserManagementService;

public abstract class DomainTestBase : AbpIntegratedTest<UserManagementServiceDomainTestModule>
{
    protected IServiceProvider ServiceProvider { get; }

    protected ICurrentTenant CurrentTenant => ServiceProvider.GetRequiredService<ICurrentTenant>();

    protected IUnitOfWorkManager UnitOfWorkManager => ServiceProvider.GetRequiredService<IUnitOfWorkManager>();

    protected IGuidGenerator GuidGenerator => ServiceProvider.GetRequiredService<IGuidGenerator>();

    protected DomainTestBase()
    {
        ServiceProvider = GetRequiredService<IServiceProvider>();
    }

    protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
    {
        options.UseAutofac();
    }

    protected virtual Task WithUnitOfWorkAsync(Func<Task> action)
    {
        return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), action);
    }

    protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func<Task> action)
    {
        using (var uow = UnitOfWorkManager.Begin(options))
        {
            await action();
            await uow.CompleteAsync();
        }
    }

    protected virtual Task<TResult> WithUnitOfWorkAsync<TResult>(Func<Task<TResult>> action)
    {
        return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), action);
    }

    protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(AbpUnitOfWorkOptions options, Func<Task<TResult>> action)
    {
        using (var uow = UnitOfWorkManager.Begin(options))
        {
            var result = await action();
            await uow.CompleteAsync();
            return result;
        }
    }

    protected virtual void WithUnitOfWork(Action action)
    {
        WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), async () =>
        {
            action();
            await Task.CompletedTask;
        }).Wait();
    }

    protected virtual TResult WithUnitOfWork<TResult>(Func<TResult> action)
    {
        return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), async () =>
        {
            return await Task.FromResult(action());
        }).Result;
    }

    protected virtual void UseTenant(Guid? tenantId, Action action)
    {
        using (CurrentTenant.Change(tenantId))
        {
            action();
        }
    }

    protected virtual async Task UseTenantAsync(Guid? tenantId, Func<Task> action)
    {
        using (CurrentTenant.Change(tenantId))
        {
            await action();
        }
    }

    protected virtual TResult UseTenant<TResult>(Guid? tenantId, Func<TResult> action)
    {
        using (CurrentTenant.Change(tenantId))
        {
            return action();
        }
    }
}